﻿using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace xycn.framework.PublishSolution
{
    /// <summary>
    /// 项目build信息
    /// </summary>
    class ProjectBuildInfo
    {
        public string porjPath = "";
        public DateTime buildTime;
        //判断某个编译项是否还存在
        public bool Exists = false;
    }

    class ProjectBuilder
    {
        /// <summary>
        /// 用于判断是否需要重新发布
        /// </summary>
        public static Dictionary<string, ProjectBuildInfo> projectBuildInfos =
            new Dictionary<string, ProjectBuildInfo>();

        /// <summary>
        /// 解决方案树中本项目对应的项目节点
        /// </summary>
        public ProjectNode projectNode = null;

        /// <summary>
        /// 项目生成路径
        /// </summary>
        //public string outputDir = "";

        /// <summary>
        /// 项目生成文件
        /// </summary>
        //public string outputFile = "";

        public static void LoadProjectBuildInfos()
        {
            string file_name = Program.RuntimeDir + "\\buildinfos.dat";
            if (!File.Exists(file_name)) return;

            using (StreamReader sr = new StreamReader(file_name))
            {
                string line;

                // 从文件读取并显示行，直到文件的末尾 
                while ((line = sr.ReadLine()) != null)
                {
                    ProjectBuildInfo projectBuildInfo = new ProjectBuildInfo();
                    projectBuildInfo.porjPath = line.Substring(0, line.IndexOf("::::"));
                    line = line.Remove(0, line.IndexOf("::::") + 4);
                    projectBuildInfo.buildTime = Convert.ToDateTime(line);

                    projectBuildInfos.Add(projectBuildInfo.porjPath,
                        projectBuildInfo);
                        }
            }
        }

        /// <summary>
        /// 保存项目生成信息
        /// </summary>
        public static void SaveProjectBuildInfos() 
        {
            string file_name = Program.RuntimeDir + "\\buildinfos.dat";
            using (StreamWriter sr = new StreamWriter(file_name))
            {
                foreach (KeyValuePair<string, ProjectBuildInfo> item in projectBuildInfos)
                {
                    string proj_file = item.Key;
                    ProjectBuildInfo build_info = item.Value;

                    if (!build_info.Exists) continue;

                        sr.WriteLine(proj_file + "::::" + 
                        build_info.buildTime.ToString());
                }

                sr.Flush();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="project"></param>
        public ProjectBuilder(ProjectNode project)
        {
            projectNode = project;
        }

        /// <summary>
        /// 处理项目
        /// </summary>
        public void Build()
        {
            if (projectNode == null ||
                projectNode.projType != SolutionProjectType.KnownToBeMSBuildFormat)
                return;

            ProjectInSolution proj_info = projectNode.projInfo;
            //根据项目信息，定位输出目录
            string proj_path = proj_info.AbsolutePath;

            Program.logFileWriter.WriteLine("[分析项目]：" + proj_info.ProjectName);

            //获取项目设置的输出路径及输出文件名
            string output_file = GetOutputDir(proj_info.ProjectName, proj_path);

            Program.logFileWriter.WriteLine(">>>>>获取生成路径：" + proj_info.ProjectName);

            //发布项目至指定目录
            Publish(proj_path, output_file);
        }

        /// <summary>
        /// 检查输出模块是否存在，如不存在，则调用dotnet build进行编译生成
        /// </summary>
        /// <param name="proj_file"></param>
        /// <returns></returns>
        private string GetOutputDir(string proj_name, string proj_file)
        {
            string ret = "";

            if (!File.Exists(proj_file))
                return ret;

            XDocument document = XDocument.Load(proj_file);

            //XElement root_node = document.Root.Element("Project");
            //获取项目属性
            IEnumerable<XElement> property_groups =
                document.Root.Elements();

            //项目地址
            string proj_dir = System.IO.Path.GetDirectoryName(proj_file) + "\\";
            //生成路径是否包含框架名
            bool apped_frameworkdir = true;
            //项目使用框架
            string framework_name = "";
            //debug模式生成路径
            string debug_outputpath = "./bin/debug";
            //release模式生成路径
            string release_outputpath = "./bin/release";
            //生成类型
            string out_type = "library";

            foreach (XElement item in property_groups)
            {
                if (item.Name != "PropertyGroup")
                    continue;
                XElement targetframework_node = item.Element("TargetFramework");
                if (targetframework_node != null)
                    framework_name = targetframework_node.Value;

                //判断输出目录是否包含平台名称
                XElement appenddir_node = item.Element("AppendTargetFrameworkToOutputPath");
                if (appenddir_node != null && appenddir_node.Value == "false")
                {
                    apped_frameworkdir = false;
                }

                //判断生成类型
                XElement outtype_node = item.Element("OutputType");
                if (outtype_node != null)
                {
                    out_type = outtype_node.Value.ToLower();
                }

                //获取输出路径
                XAttribute propertygroup_attr = item.Attribute("Condition");
                if (propertygroup_attr != null)
                {
                    if (propertygroup_attr.Value.ToLower().IndexOf("debug") >= 0)
                    {
                        XElement output_node = item.Element("OutputPath");
                        if (output_node != null && output_node.Value != "")
                        {
                            debug_outputpath = output_node.Value;
                        }
                    }

                    if (propertygroup_attr.Value.ToLower().IndexOf("release") >= 0)
                    {
                        XElement output_node = item.Element("OutputPath");
                        if (output_node != null && output_node.Value != "")
                        {
                            release_outputpath = output_node.Value;
                        }
                    }
                }
            }

            //构建生成文件绝对路径
            if (Program.compileType == CompileType.Debug)
            {
                Uri uri_p3 = new Uri(proj_dir, UriKind.Absolute);
                Uri uri_P4 = new Uri(debug_outputpath, UriKind.Relative);
                Uri uri_result = new Uri(uri_p3, uri_P4);
                ret = uri_result.LocalPath;
            }
            else
            {
                Uri uri_p3 = new Uri(proj_dir, UriKind.Absolute);
                Uri uri_P4 = new Uri(release_outputpath, UriKind.Relative);
                Uri uri_result = new Uri(uri_p3, uri_P4);
                ret = uri_result.LocalPath;
            }

            if (apped_frameworkdir)
            {
                ret = ret + "\\" + framework_name;
            }
            if (out_type == "library")
            {
                ret = ret + "\\" + proj_name + ".dll";
            }
            else
            {
                ret = ret + "\\" + proj_name + "." + out_type;
            }
            return ret;
        }

        /// <summary>
        /// 将依赖一起打入到项目发布目录
        /// </summary>
        /// <param name="proj_file"></param>
        /// <param name="output_file"></param>
        private void Publish(string proj_file, string output_file)
        {
            //hack

            //hack over
            if (Program.compileType == CompileType.Release)
            {
                //ExecuteDotnet("build " + proj_file);
                ExecuteDotnet("clean -o " + projectNode.targetBuildDir + " " + proj_file);
                ExecuteDotnet("publish -c Release -o " + projectNode.targetBuildDir + " " + proj_file);
            }
            else
            {

                bool do_build = false;
                ProjectBuildInfo projectBuildInfo = null;
                if (!projectBuildInfos.ContainsKey(proj_file))
                {
                    do_build = true;

                    projectBuildInfo = new ProjectBuildInfo();
                    projectBuildInfo.porjPath = proj_file;
                    projectBuildInfo.Exists = true;
                    projectBuildInfos.Add(proj_file, projectBuildInfo);
                }
                else
                {
                    projectBuildInfo = projectBuildInfos[proj_file];
                    projectBuildInfo.Exists = true;

                    if (!Directory.Exists(projectNode.targetBuildDir))
                    {
                        //新项目
                        do_build = true;
                    }
                    else
                    {
                        string publish_file = projectNode.targetBuildDir + "\\" +
                            projectNode.projInfo.ProjectName + ".dll";
                        //项目发布时在原生成路径依旧会创建项目生成文件，
                        //同时，如果采用非发布方式如通过ide直接编译，该文件仍然会杯改变，
                        //因此可以用该文件作为是否变更的检查条件
                        FileInfo outputfile_info = new FileInfo(output_file);
                        FileInfo publishfile_info = new FileInfo(publish_file);
                        if (outputfile_info.Exists == false ||
                            publishfile_info.Exists == false)
                        {
                            do_build = true;
                        }
                        else
                        {
                            //根据上次该项目生成时间，同输出文件时间之间的差值，
                            //判断项目是否发生改变
                            TimeSpan modify_span1 = new TimeSpan(outputfile_info.LastWriteTime.Ticks
                                 - projectBuildInfo.buildTime.Ticks);

                            TimeSpan modify_span2 = new TimeSpan(outputfile_info.LastWriteTime.Ticks
                                 - publishfile_info.LastWriteTime.Ticks);

                            if (modify_span1.TotalSeconds > 20 || modify_span1.TotalSeconds < -20 ||
                                modify_span2.TotalSeconds > 20 || modify_span2.TotalSeconds < -20)
                            {
                                do_build = true;
                            }
                        }
                    }
                }

                if (do_build)
                {
                    //ExecuteDotnet("build " + proj_file);
                    ExecuteDotnet("clean -o " + projectNode.targetBuildDir + " " + proj_file);
                    ExecuteDotnet("publish -o " + projectNode.targetBuildDir + " " + proj_file);

                    projectBuildInfo.buildTime = DateTime.Now;
                }
            }
        }

        /// <summary>
        /// 执行Build命令
        /// </summary>
        private void ExecuteDotnet(string args)
        {
            Process p = new Process();
            try
            {
                //设置要启动的应用程序
                p.StartInfo.FileName = "dotnet";

                p.StartInfo.Arguments = args;
                //是否使用操作系统shell启动
                p.StartInfo.UseShellExecute = false;
                // 接受来自调用程序的输入信息
                p.StartInfo.RedirectStandardInput = false;
                //输出信息
                p.StartInfo.RedirectStandardOutput = false;
                // 输出错误
                p.StartInfo.RedirectStandardError = true;
                //不显示程序窗口
                p.StartInfo.CreateNoWindow = false;
                //启动程序
                p.Start();
                //向cmd窗口发送输入信息
                //p.StandardInput.WriteLine(str.ToString());
                //p.StandardInput.WriteLine("\n");
                //p.StandardInput.WriteLine("exit\n");
                p.WaitForExit();
            }
            finally
            {
                p.Close();
            }
        }
    }
}
